#coding=utf-8
from django.shortcuts import render_to_response,render
from django.http import HttpResponseRedirect,HttpResponse
from django.template import RequestContext
import MySQLdb
import string,StringIO
import csv
import xlrd  
import os
import numpy as np
import pandas as pd
import operator
import json
import random
from math import log


def sql_select(text):  
    conn=MySQLdb.connect(host='localhost',user="root",passwd="piclass",db="sjfx",port=3306,charset='utf8')
    cur = conn.cursor()
    cur.execute(text)
    res=cur.fetchall()
    cur.close()
    conn.commit()
    conn.close()
    return res
def sql_write(text):
    conn=MySQLdb.connect(host='localhost',user="root",passwd="piclass",db="sjfx",port=3306,charset='utf8')
    cur = conn.cursor()
    cur.execute(text)
    cur.close()
    conn.commit()
    conn.close()

def distance(x,y):    #欧式距离计算函数
    result = flag = 0
    for i in range(len(x)):     # i维的距离计算
        flag = (x[i]-float(y[i]))**2   
        result += flag
    return result**0.5   # float型

def knn_model(train_set,test_set,k):   # KNN算法（训练集最后一列为类别； 第一行为表头）
    min_distance = distance(test_set,train_set[1])  # 第0行为表头,跳过 
    shuxing = train_set[0][:-1] # 保存所有属性值（最后一列为类别，不保存）
    all_labels = [train_set[1][-1]]  # 保存所有不同的类别       
    label = train_set[1][-1]
    temp_dis = [min_distance]      # 用于距离排序的列表
    temp_label = {min_distance:label}  # 保存距离相应的label(字典)
    for t in train_set[2:]:
        dis = distance(test_set,t)
        temp_dis.append(dis)
        temp_label[dis] = t[-1]
        if all_labels.count(t[-1]) == 0:  # 如果该类别没有出现，则添加进去
            all_labels.append(t[-1]) 
    temp_dis.sort()      # 距离升序的列表
    label = [temp_label[temp_dis[0]]]  # k个距离最近对象所对应的标签(列表)
    for i in range(1,k):
        label.append(temp_label[temp_dis[i]]) 
    flag_label = label[0]      # falg保存最终类别
    for i in range(k):
        if flag_label < label.count(label[i]):
            flag_label = label[i]
    return flag_label,all_labels,shuxing


def lagrange_model(testset_y,testset_x,n,value_x,k):    # 拉格朗日插值函数
    '''
    testset_y(x):纵(横)坐标组成的向量；n:被插值的位置；value_x:缺失值的横坐标；k:取前后数据的个数
    '''
    x = list(testset_x[n-k:n]) + list(testset_x[n+1:n+1+k])
    y = list(testset_y[n-k:n]) + list(testset_y[n+1:n+1+k])   
    x1 = []
    y1 = []
    for i in range(len(y)):    # 剔除所选范围内的空值
        if y[i] != '':
             y1.append(y[i])
             x1.append(x[i])
    result = 0.0      # 保存插值结果
    for i in range(len(y1)):
        t = y1[i]
        for j in range(len(y1)):
            if i != j:
                t *= (float(value_x)-x1[i])/(x1[j]-x1[i])     # float(a) !不可为int
        result += t 
    return result

def data_isnull(testset_x,testset_y,k):   # 判断是否需要插值
    res = []
    for i in range(len(testset_y)):   # j为行
        if testset_y[i] == '':
            res.append([testset_x[i],lagrange_model(testset_y,testset_x,i,testset_x[i],k)])    # 被插值的列/缺失值的位置
    return res




def random_k(data,k):    # 随机选取初始的类中心(k-means)
    center=[]
    a=0
    if k>=len(data):
        print"k值过大"
        return 0
    c=len(data)/k
    for a in range(k):
       center.append(a*c+random.randint(0,c-1)+1)
    return center    # 初始聚类的中心组号列表


def centrel_data(data,record,):     # 求类的中心(k-means)
    centrel=[]
    for x in range(len(data[0])):
        centrel.append(0)
        for a in record:
            centrel[x]+= data[a][x]
        centrel[x]/=len(record)
    return  centrel
    

def kmeans_model(data,k,b):
    record=[]
    if k==len(data):
        for i in range(len(data)):
            record.append([i])
        return record
        
    x=0         # x 为循环总次数
    j=0
    centrel=[[]]
    for i in b:         #创建初始类的中心
        centrel[x].append(data[i])
    for x in range(len(data)):        
        record.append([])
        for j in range(k):
            record[x].append([])        
        for i in range(len(data)):          #将各组数据归类
            y=0
            dis0=distance(data[i],centrel[x][y])
            for j in range(k):              #寻找最近的类中心
                dis=distance(data[i],centrel[x][j])
                if dis<dis0:
                    dis0=dis
                    y=j
            record[x][y].append(i)          #将组号添加到类中
        centrel.append([])
        suc=0
        for y in range(k):         #计算类的中心
            centrel[x+1].append(centrel_data(data,record[x][y]))
            if abs(cmp(centrel[x+1][y],centrel[x][y]))!=0:      #检查中心是否有变化
                suc=1
        if suc==0:
            return record
            break


def pca_model(X,k):    # 二维训练集/降到k维
    X = np.array(X)
    n_samples, n_features = X.shape
    mean = np.array([np.mean(X[:,i]) for i in range(n_features)])  # 求平均值
    norm_X = X - mean   #一般化
    scatter_matrix = np.dot(np.transpose(norm_X),norm_X)  #做散射矩阵
    eig_val, eig_vec = np.linalg.eig(scatter_matrix)   #计算特征值和特征向量
    eig_pairs = [(np.abs(eig_val[i]), eig_vec[:,i]) for i in range(n_features)]
    eig_pairs.sort(reverse=True)  # 从大到小的选择K个特征值对应的特征向量
    feature = np.array([ele[1] for ele in eig_pairs[:k]])   # 选择最大的特征向量
    result = np.dot(norm_X,np.transpose(feature))  #转化得到降维以后的新的数据
    return result

#X = np.array([[-1, 1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])  #给出二维的数据训练集 
#print pca(X,1)


#构造决策树，采用Python字典来递归构造
def treeGrowth(dataSet,features):  
    classList = [example[-1] for example in dataSet]  
    if classList.count(classList[0])==len(classList):  
        return classList[0]  
    if len(dataSet[0])==1:  
        return classify(classList)  
  
    bestFeat = findBestSplit(dataSet) 
    bestFeatLabel = features[bestFeat]  
    myTree = {bestFeatLabel:{}}  
    featValues = [example[bestFeat] for example in dataSet]  
    uniqueFeatValues = set(featValues)  
    del (features[bestFeat])  
    for values in uniqueFeatValues:  
        subDataSet = splitDataSet(dataSet,bestFeat,values)  
        myTree[bestFeatLabel][values] = treeGrowth(subDataSet,features)  
    return myTree 

#当没有多余的feature，但是剩下的样本不完全是一样的类别是，采用出现次数多的那个类别
def classify(classList):  
    ''''' 
    find the most in the set 
    '''  
    classCount = {}  
    for vote in classList:  
        if vote not in classCount.keys():  
            classCount[vote] = 0  
        classCount[vote] += 1  
    sortedClassCount = sorted(classCount.iteritems(),key = operator.itemgetter(1),reverse = True)  
    return sortedClassCount[0][0]  
 
#寻找用于分裂的最佳属性：（遍历所有属性，算信息增益）
def findBestSplit(dataset):  
    numFeatures = len(dataset[0])-1  
    baseEntropy = calcShannonEnt(dataset)  
    bestInfoGain = 0.0  
    bestFeat = -1  
    for i in range(numFeatures):  
        featValues = [example[i] for example in dataset]  
        uniqueFeatValues = set(featValues)  
        newEntropy = 0.0  
        for val in uniqueFeatValues:  
            subDataSet = splitDataSet(dataset,i,val)  
            prob = len(subDataSet)/float(len(dataset))  
            newEntropy += prob*calcShannonEnt(subDataSet)  
        if(baseEntropy - newEntropy)>bestInfoGain:  
            bestInfoGain = baseEntropy - newEntropy  
            bestFeat = i  
    return bestFeat 
    
#选择完分裂属性后，就行数据集的分裂
def splitDataSet(dataset,feat,values):  
    retDataSet = []  
    for featVec in dataset:  
        if featVec[feat] == values:  
            reducedFeatVec = featVec[:feat]  
            reducedFeatVec.extend(featVec[feat+1:])  
            retDataSet.append(reducedFeatVec)  
    return retDataSet 

#计算数据集的熵
def calcShannonEnt(dataset):  
    numEntries = len(dataset)  
    labelCounts = {}  
    for featVec in dataset:  
        currentLabel = featVec[-1]  
        if currentLabel not in labelCounts.keys():  
            labelCounts[currentLabel] = 0  
        labelCounts[currentLabel] += 1  
    shannonEnt = 0.0  
  
    for key in labelCounts:  
        prob = float(labelCounts[key])/numEntries  
        if prob != 0:  
            shannonEnt -= prob*log(prob,2)  
    return shannonEnt  

#下面根据上面构造的决策树进行数据的分类
def ID3_predict(tree,newObject):  
    while isinstance(tree,dict):  
        key = tree.keys()[0]  
        tree = tree[key][newObject[key]]  
    return tree  
#决策树完


def evaluate_yy(data,aver_y):
    yy=0
    for i in range(len(data)):
        yy+=(data[i][1]-aver_y)**2
    return yy

def evaluate_xx(data,aver_x):
    xx=0
    for i in range(len(data)):
        xx+=(data[i][0]-aver_x)**2
    return xx

def evaluate_xy(data,aver_x,aver_y):
    xy=0
    for i in range(len(data)):
        xy+=(data[i][1]-aver_y)*(data[i][0]-aver_x)
    return xy

def average_(data,a):
    aver=0
    for i in range(len(data)):
        aver+=data[i][a]
    return aver/(len(data)+1)
#回归分析完



def login(request):  # 登录
    if request.method == 'GET':
        try:
            request.session['username']='anonymous'
            
        except:
            pass
        return render(request,'login.html')
    if request.method == 'POST':
        i = request.POST
        if i.has_key('login'):
            username = i['username'].replace(u' ',u'')   
            password = i['password'].replace(u' ',u'')
            res = sql_select("SELECT * from user where username ='%s' and password='%s'"%(username,password)) 
            if len(res) != 0:        # 验证用户名、密码
                request.session['username'] = res[0][1]
                request.session['password'] = res[0][2]
                #return HttpResponse('ok,%s' %request.session.password)  # test
                #return render(request,'index.html')
                return render_to_response("index.html",{'user':username})
            else:
                return render_to_response("login.html",{'i':len(res)},context_instance=RequestContext(request))  # 登录失败
        elif i.has_key('signup'):   # 注册
            return HttpResponseRedirect('/signup.html/')
        else:  # 忘记密码
            pass
        

def signup(request):  # 注册
    if request.method == 'GET':
        return render(request,'signup.html')   
    if request.method == 'POST':
        i = request.POST
        if i.has_key('confirm'):   # 确认注册按钮
            name = i['new_user'].replace(u' ',u'')
            pwd1 = i['pwd1'].replace(u' ',u'')
            pwd2 = i['pwd2'].replace(u' ',u'')
            email = i['new_email'].replace(u' ',u'')
            res = sql_select("SELECT * from user where username ='%s'"%(name))
            if len(res) == 0:   # 用户名不重复
                if pwd1 == pwd2:  # 两次密码一致
                    sql_write("INSERT INTO user(username,password,email) VALUES('%s','%s','%s');"%(name,pwd1,email))
                    return HttpResponseRedirect('/succeed_signup.html/')  # 注册成功
                else:
                    return render_to_response("signup.html",{'i':1,'name':name,'pwd1':pwd1,'pwd2':pwd2,'email':email},context_instance=RequestContext(request))  # 密码不一致   这两种情况还需保留表单原有内容，未完成
            else:
                return render_to_response("signup.html",{'res':len(res),'name':name,'pwd1':pwd1,'pwd2':pwd2,'email':email},context_instance=RequestContext(request))  # 用户已存在
        else:
            return render_to_response('wrong')


def succeed_signup(request):
    return render(request,'succeed_signup.html')


def  index(request):                # 主页         
    if request.method == "GET":
        return render_to_response("index.html",{'user':request.session['username']})    
    if request.method == "POST":
        pass



def analysis(request):  # 数据分析
    if request.method == "GET":
        return render(request,"analysis.html")

def knn(request):
    if request.method == "GET":
        return render(request,"knn.html",{'result':''})
    if request.method == 'POST':
        i = request.POST
        if i.has_key('ok'):  
            csv_file = request.FILES["csv"].read()
            fobj = StringIO.StringIO(csv_file) # 获取上传的文件 
            csv_result = list(csv.reader(fobj))
            train_set = csv_result  # 训练集（或为用户要分析的文件）
            test_set = (i['testset_input']).split(",")
            test_set = map(float,test_set)
            k = int(request.POST['value_k'])

            label,all_labels,shuxing = knn_model(train_set,test_set,k)


            all_labels.append('待测对象')
            len_labels = len(all_labels) - 1
            train_set2 = []       # 保存数字部分
            per_label = []    # 每一项对应的类别
            for i in train_set[1:]:
                per_label.append(i[-1])  
                train_set2.append(map(float,i[:-1]))# 除去类别，只保留数字
            '''
            data = []   # 传给echarts
            for i in range(len(train_set2)):
                data.append({'value':train_set2[i],'name':per_label[i]})
            data = json.dumps(data,ensure_ascii=False)  
            '''
            if len(shuxing) > 2:  #采用雷达图
                all_labels = json.dumps(all_labels,ensure_ascii=False)

                data = []   # 传给echarts
                for i in range(len(train_set2)):
                    data.append({'value':train_set2[i],'name':per_label[i]})
                data.append({'value':test_set,'name':'待测对象'})
                data = json.dumps(data,ensure_ascii=False)  

                indicator= []  # 传给echarts
                numb = np.array(train_set2)
                numb = numb.transpose()
                for i in range(len(shuxing)):
                    indicator.append({'text' :shuxing[i],'max' :max(numb[i])})
                indicator = json.dumps(indicator,ensure_ascii=False)  
                return render_to_response("knn.html",{'length':len(shuxing),
                                                'k':k,
                                                'test_set':test_set,
                                                'label':label,
                                                'indicator':indicator,
                                                'all_labels':all_labels,
                                                'len_labels':len_labels,
                                                'data':data},
                                                context_instance=RequestContext(request))

            elif len(shuxing) == 2:  # 散点图
                data = []
                series = []
                for i in range(len(all_labels) - 1):  # 所有不同类
                    for j in range(len(per_label)):  # 每个item对应的类
                        if per_label[j] == all_labels[i]:    # 找出同类的item，存为一组
                            series.append(train_set2[j])
                    data.append({'name':all_labels[i],'type':'scatter','data':series,'symbolSize':10})
                    series = []

                data.append({'name':'待测对象','type':'scatter','data':[test_set],'symbolSize':10}) #
                data = json.dumps(data,ensure_ascii=False)
                all_labels = json.dumps(all_labels,ensure_ascii=False)
                return render_to_response("knn.html",{'length':len(shuxing),
                                                'k':k,
                                                'test_set':test_set,
                                                'label':label,
                                                'shuxing0':json.dumps(shuxing[0],ensure_ascii=False),
                                                'shuxing1':json.dumps(shuxing[1],ensure_ascii=False),
                                                'all_labels':all_labels,
                                                'len_all':len(all_labels),
                                                'len_labels':len_labels,
                                                'data':data},
                                                context_instance=RequestContext(request))

            

def lagrange(request):    
    if request.method == "GET":
        return render(request,"lagrange.html")
    if request.method == 'POST':
        i = request.POST
        if i.has_key('ok'):  # 点击上传
            csv_file = request.FILES["csv"].read()
            fobj = StringIO.StringIO(csv_file) # 获取上传的文件 
            csv_result =list(csv.reader(fobj))
            csv_result = csv_result[1:]   # 除去表头
            k = int(request.POST['value_k'])
            lag_list_x = []
            lag_list_y = []

            for i in range(len(csv_result)):
                if csv_result[i][1] != '':
                    csv_result[i] = map(float,csv_result[i])
                else:
                    csv_result[i][0] = float(csv_result[i][0])
                    csv_result[i][1] = ''
            for i in csv_result:
                lag_list_x.append(i[0])
                lag_list_y.append(i[1])

            result = data_isnull(lag_list_x,lag_list_y,k)

            data = [{'name':'原始数据','type':'scatter','data':csv_result,'symbolSize':8},
                    {'name':'插值结果','type':'scatter','data':result,'symbolSize':8}]
            data = json.dumps(data,ensure_ascii=False)
            return render_to_response('lagrange.html',{'result':result,
                                                        'k':k,
                                                        'lag_list_x':lag_list_x,
                                                        'lag_list_y':lag_list_y,
                                                        'data':data,
                                                        'csv':csv_result},
                                                        context_instance=RequestContext(request))


def pca(request):
    if request.method == "GET":
        return render(request,"PCA.html")
    if request.method == 'POST':
        i = request.POST
        if i.has_key('ok'):  # 点击上传
            csv_file = request.FILES["csv"].read()
            fobj = StringIO.StringIO(csv_file) # 获取上传的文件 
            csv_result =list(csv.reader(fobj))
            n1 = int(request.POST['n1'])
            n2 = int(request.POST['n2'])

            Xname = json.dumps(csv_result[0][0],ensure_ascii=False)
            Yname = json.dumps(csv_result[0][1],ensure_ascii=False)
            shuxing = csv_result[0]
            csv_result = csv_result[1:]

            for i in range(len(csv_result)):
                csv_result[i] = map(float,csv_result[i])
            
            result = pca_model(csv_result,n2)
            res = []
            for i in result:
                res.append([float(i),0])

            data1 = [{'name':'原始数据','type':'scatter','data':csv_result,'symbolSize':8}]
            data1 = json.dumps(data1,ensure_ascii=False)
            data2 = [{'name':'PCA分析结果','type':'scatter','data':res,'symbolSize':8}]
            data2 = json.dumps(data2,ensure_ascii=False) 

            return render_to_response('PCA.html',{'result':res,
                                                    'data1':data1,
                                                    'data2':data2,
                                                    'Xname':Xname,
                                                    'Yname':Yname,
                                                    'n1':n1,
                                                    'n2':n2,
                                                        },
                                                        context_instance=RequestContext(request))



def decision_tree(request):               
    if request.method == "GET":
        return render(request,"decision_tree.html")
    if request.method == 'POST':
        i = request.POST
        if i.has_key('ok'):  # 点击上传
            csv_file = request.FILES["csv"].read()
            fobj = StringIO.StringIO(csv_file) # 获取上传的文件 
            csv_result =list(csv.reader(fobj))
            shuxing = csv_result[0][:-1] 
            csv_result = csv_result[1:]
            
            for i in range(len(csv_result)):
                for j in range(len(csv_result[0]) - 1):
                    csv_result[i][j] = float(csv_result[i][j])
            
            test_set = (request.POST['testset_input']).split(",")
            test_set = map(float,test_set)
            test_set2 = {}  # 最后test_set2[0]为测试集
            for i in range(len(shuxing)):
                test_set2[shuxing[i]] = float(test_set[i])
            

            tree = treeGrowth(csv_result,shuxing)  # 构造树
            result = ID3_predict(tree,test_set2)   # 测试集的分类结果
            data = []
            data.append(json.dumps(tree,ensure_ascii=False))
            
            
             
            return render_to_response('decision_tree.html',{'result':result,
                                                            'csv':csv_result,
                                                            'tree':tree,
                                                            'data':data,
                                                            },
                                                            context_instance=RequestContext(request))


def k_means(request):
    if request.method == "GET":
        return render(request,"k-means.html")
    if request.method == 'POST':
        i = request.POST
        if i.has_key('ok'):  # 点击上传
            k = int(request.POST['value_k'])
            csv_file = request.FILES["csv"].read()
            fobj = StringIO.StringIO(csv_file) # 获取上传的文件 
            csv_result = list(csv.reader(fobj))
            
            Xname = json.dumps(csv_result[0][0],ensure_ascii=False)
            Yname = json.dumps(csv_result[0][1],ensure_ascii=False)
            shuxing = csv_result[0]
            csv_result = csv_result[1:]

            for i in range(len(csv_result)):
                csv_result[i] = map(float,csv_result[i])
            
            b = random_k(csv_result,k)
            record=kmeans_model(csv_result,k,b)                             #整个过程 记录在record 中
            a=len(record)-1

            result=[]         # 聚类结果的2维列表
            for i in range(len(record[a])):
                result.append([ ])
                for j in range(len(record[a][i])):
                    result[i].append(csv_result[record[a][i][j]])


            labels = []   # 传给echarts
            for i in range(k):
                flag = 'Group '+str(i+1)
                labels.append(flag)


            if len(csv_result[0]) == 2:   # 散点图
                data=[]    # 传给echarts
                for i in range(len(labels)):
                    data.append({'name':labels[i],'type':'scatter','data':result[i],'symbolSize':8})
                data = json.dumps(data,ensure_ascii=False)

                return render_to_response('k-means.html',{'result':result,
                                                        'data':data,
                                                        'labels':labels,
                                                        'k':k,
                                                        'Xname':Xname,
                                                        'Yname':Yname,
                                                        'length':len(csv_result[0]),
                                                        },
                                                            context_instance=RequestContext(request))


            elif len(csv_result[0]) > 2:   # 雷达图
                data = []
                per_label = []   
                for i in range(len(result)):
                    for j in result[i]:
                        flag = 'Group '+str(i+1)
                        per_label.append(flag)

                for i in range(len(csv_result)):
                    data.append({'name':per_label[i],'value':csv_result[i]})
                data = json.dumps(data,ensure_ascii=False)

                numb = np.array(csv_result)   # 文件中所有数字
                numb = numb.transpose()     # 矩阵转置
                indicator = []  # 传给echarts
                for i in range(len(shuxing)):
                    indicator.append({'text':shuxing[i],'max':max(numb[i])})

                return render_to_response('k-means.html',{'result':result,
                                                        'data':data,
                                                        'labels':labels,
                                                        'k':k,
                                                        'indicator':indicator,
                                                        'length':len(csv_result[0]),
                                                        },
                                                            context_instance=RequestContext(request))




def RA_line(request):    
    if request.method == "GET":
        return render(request,"RA_line.html")
    if request.method == 'POST':
        i = request.POST
        if i.has_key('ok'):  # 点击上传
            csv_file = request.FILES["csv"].read()
            fobj = StringIO.StringIO(csv_file) # 获取上传的文件 
            csv_result =list(csv.reader(fobj))
            x = float(request.POST['x'])
            Xname = json.dumps(csv_result[0][0],ensure_ascii=False)
            Yname = json.dumps(csv_result[0][1],ensure_ascii=False)
            csv_result = csv_result[1:]   # 除去表头
            for i in range(len(csv_result)):
                csv_result[i] = map(float,csv_result[i])
            data = json.dumps(csv_result,ensure_ascii=False)


            aver_x = average_(csv_result,0)           # x 均值
            aver_y = average_(csv_result,1)           # 均值
            xx = evaluate_xx(csv_result,aver_x)           
            yy = evaluate_yy(csv_result,aver_y)           # TSS 总离差平方和
            xy = evaluate_xy(csv_result,aver_x,aver_y)    # 

            a  =xy/xx                         #回归方程 斜率
            b = aver_y-a*aver_x               #回归方程 截距
            sse = yy-a*xy                     # 结果分析   残差平方和
            r = xy/(xx*yy)**0.5               # 结果分析   相关系数
            s = 1-(sse/yy)
            result = a*x+b                    # 结果分析   拟合系数
            return render_to_response('RA_line.html',{'result':result,
                                                        'data':data,
                                                        'x':x,
                                                        'Xname':Xname,
                                                        'Yname':Yname,
                                                        'r':r, #相关系数
                                                        's':s, #拟合系数
                                                        'a':a,
                                                        'b':b,
                                                        'b2':abs(b),
                                                        },
                                                            context_instance=RequestContext(request))

                

def help2(request):   # 帮助中心
    if request.method == "GET":
        return render_to_response("help.html",{'user':request.session['username']})


def teach(request):   # 教学辅助
    if request.method == "GET":
        return render_to_response("teach.html",{'user':request.session['username']})



